home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Utilities / Ph 1.1.1 / PhClient / rslv.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-29  |  16.9 KB  |  578 lines  |  [TEXT/MPS ]

  1. /*_____________________________________________________________________
  2.  
  3.       rslv.c - Domain name resolver.
  4.     
  5.     This is my own home-grown name resolver. It is not used in the current
  6.     version of Ph. (All calls to it in mtcp.c have been commented out.)
  7.     It doesn't work with MacTCP 1.1.1, presumably because the format of the
  8.     dnsl 128 resource has changed.
  9.     
  10.     I'm leaving the source code here in case I need it some day.
  11. _____________________________________________________________________*/
  12.  
  13. /*_____________________________________________________________________
  14.  
  15.     Header Files.
  16. _____________________________________________________________________*/
  17.  
  18. #pragma load "precompile"
  19. #include <MacTCPCommonTypes.h>
  20. #include <UDPPB.h>
  21. #include "rslv.h"
  22. #include "utl.h"
  23. #include "fsu.h"
  24.  
  25. #pragma segment rslv
  26.  
  27. /*_____________________________________________________________________
  28.  
  29.     Constants.
  30. _____________________________________________________________________*/
  31.  
  32. #define MacTCPFileName        "\pMacTCP"        /* MacTCP file name */
  33. #define initialTimeout        4                    /* initial timeout delay in seconds */
  34. #define maxQueries            4                    /* max number of query attempts */
  35. #define rcvBuffSize            2048                /* size of UDP receive buffer */
  36. #define typeA                    1                    /* DNS "A" type code */
  37. #define classIN                1                    /* DNS "IN" class code */
  38. #define ourTypeClass            ((typeA<<16)|classIN)    /* "A" type code and "IN" class code */
  39. #define dnsPort                53                    /* DNS UDP port number */
  40. #define dnsNameError            3                    /* DNS "name error" response code */
  41.  
  42. /*_____________________________________________________________________
  43.  
  44.     Types.
  45. _____________________________________________________________________*/
  46.  
  47. typedef struct DNSQuery {
  48.     unsigned short            id;                /* identifier */
  49.     unsigned short            flags;            /* header flags word */
  50.     unsigned short            qdCount;            /* question count */
  51.     unsigned short            anCount;            /* answer count */
  52.     unsigned short            nsCount;            /* authority count */
  53.     unsigned short            arCount;            /* additional count */
  54.     unsigned char            question[260];    /* the question */
  55. } DNSQuery;
  56.  
  57. typedef struct DNSReply {
  58.     unsigned short            id;                /* identifier */
  59.     unsigned short            flags;            /* header flags word */
  60.     unsigned short            qdCount;            /* question count */
  61.     unsigned short            anCount;            /* answer count */
  62.     unsigned short            nsCount;            /* authority count */
  63.     unsigned short            arCount;            /* additional count */
  64.     unsigned char            sections[];        /* the sections */
  65. } DNSReply;
  66.  
  67. typedef struct WDS {
  68.     short                        length;            /* buffer length */
  69.     Ptr                        buffer;            /* pointer to buffer */
  70.     short                        zero;                /* terminating zero word */
  71. } WDS;
  72.  
  73. /*_____________________________________________________________________
  74.  
  75.     Global Variables.
  76. _____________________________________________________________________*/
  77.  
  78. /* The server list is maintained in a single relocatable block containing
  79.     a list of the server IP addresses. A server address may be set to 0
  80.     to remove it from the list (e.g., if it returns a bogus reply). */
  81.     
  82. static Handle                ServerList;            /* handle to server list */
  83. static short                ServerListSize;    /* size of server list */
  84.  
  85. /* The cache is maintained in a single relocatable block containing
  86.     a list of entries. Each entry is a Pascal format domain name string
  87.     followed by its longword IP address. */
  88.  
  89. static Handle                Cache;            /* handle to cache */
  90. static short                CacheSize;        /* size of cache */
  91.  
  92. static Str255                Extension;        /* default domain name extension */
  93. static short                RefNum;            /* MacTCP driver refnum */
  94. static unsigned short    QueryId = 0;    /* DNS query identifier */
  95.  
  96. /*_____________________________________________________________________
  97.  
  98.     rslv_DottedDecimal - Convert IP Address to Dotted Decimal String.
  99.     
  100.     Entry:    addr = IP address.
  101.                 str = pointer to string buffer.
  102.     
  103.     Exit:        str = converted string, C format.
  104.                 function result = str.
  105. _____________________________________________________________________*/
  106.  
  107. unsigned char *rslv_DottedDecimal (unsigned long addr, unsigned char *str)
  108.  
  109. {
  110.     short        o1,o2,o3,o4;
  111.     
  112.     o1 = (addr >> 24) & 0xff;
  113.     o2 = (addr >> 16) & 0xff;
  114.     o3 = (addr >> 8) & 0xff;
  115.     o4 = addr & 0xff;
  116.     sprintf(str, "%d.%d.%d.%d", o1, o2, o3, o4);
  117.     return str;
  118. }
  119.  
  120. /*_____________________________________________________________________
  121.  
  122.     rslv_Init - Initialize.
  123.     
  124.     Entry:    refNum = MacTCP driver refnum.
  125. _____________________________________________________________________*/
  126.  
  127. #pragma segment rslvi
  128.  
  129. OSErr rslv_Init (short refNum)
  130.  
  131. {
  132.     FSSpec                fSpec;            /* MacTCP file spec */
  133.     short                    rsrcRefNum;        /* resource file refnum */
  134.     OSErr                    rCode;            /* error code */
  135.     Handle                h;                    /* handle to dnsl resource */
  136.     unsigned char        *p;                /* pointer into dnsl resource */
  137.     short                    ct;                /* number of items in dnsl resource */
  138.     unsigned char        *s;                /* pointer into server list */
  139.     unsigned char        *sEnd;            /* pointer to end of server list */
  140.     short                    i;                    /* loop index */
  141.     Boolean                def;                /* true if default server */
  142.     short                    len;                /* length of string */
  143.     unsigned long        addr;                /* IP address */
  144.     HParamBlockRec        pBlock;            /* PBHGetFInfo param block */
  145.     
  146.     RefNum = refNum;
  147.     
  148.     /* Open the MacTCP resource file with read-only permission,
  149.         read the dnsl id=128 resource, and initialize the server list.
  150.         Look for the MacTCP file first in the Control panels folder, then in the
  151.         System folder. If this doesn't work, look for any file with type
  152.         'cdev' and creator 'ztcp' or 'mtcp' in the System folder, in case the
  153.         user has renamed the MacTCP control panel. */
  154.     
  155.     utl_CopyPString(fSpec.name, MacTCPFileName);
  156.     if (rCode = fsu_FindFolder(kOnSystemDisk, kControlPanelFolderType, kDontCreateFolder,
  157.         &fSpec.vRefNum, &fSpec.parID)) return rCode;
  158.     if (rCode = fsu_FSpOpenResFile(&fSpec, fsRdPerm, &rsrcRefNum)) {
  159.         if (rCode = fsu_FindFolder(kOnSystemDisk, kSystemFolderType,
  160.             kDontCreateFolder, &fSpec.vRefNum, &fSpec.parID)) return rCode;
  161.         if (rCode = fsu_FSpOpenResFile(&fSpec, fsRdPerm, &rsrcRefNum)) {
  162.             pBlock.fileParam.ioNamePtr = fSpec.name;
  163.             pBlock.fileParam.ioVRefNum = fSpec.vRefNum;
  164.             pBlock.fileParam.ioFDirIndex = 1;
  165.             pBlock.fileParam.ioDirID = fSpec.parID;
  166.             while (!(rCode = PBHGetFInfo(&pBlock, false))) {
  167.                 if (pBlock.fileParam.ioFlFndrInfo.fdType == 'cdev' &&
  168.                     (pBlock.fileParam.ioFlFndrInfo.fdCreator == 'ztcp' ||
  169.                     pBlock.fileParam.ioFlFndrInfo.fdCreator == 'mtcp')) break;
  170.                 pBlock.fileParam.ioFDirIndex++;
  171.                 pBlock.fileParam.ioDirID = fSpec.parID;
  172.             }
  173.             if (rCode) return rCode;
  174.             if (rCode = fsu_FSpOpenResFile(&fSpec, fsRdPerm, &rsrcRefNum)) return rCode;
  175.         }
  176.     }
  177.     h = GetResource('dnsl', 128);
  178.     if (!h) return ResError();
  179.     HLock(h);
  180.     p = *h;
  181.     ct = *(short*)p;
  182.     p += 2;
  183.     ServerList = NewHandle(0);
  184.     *Extension = ServerListSize = 0;
  185.     for (i = 0; i < ct; i++) {
  186.         addr = *(long*)p;
  187.         p += 4;
  188.         def = *p++;
  189.         len = strlen(p);
  190.         if (len > 254) break;
  191.         if (def) {
  192.             *Extension = len+1;
  193.             *(Extension+1) = '.';
  194.             memcpy(Extension+2, p, len);
  195.         }
  196.         s = *ServerList;
  197.         sEnd = s + ServerListSize;
  198.         while (s < sEnd) {
  199.             if (addr == *(unsigned long*)s) break;
  200.             s += 4;
  201.         }
  202.         if (s >= sEnd) {
  203.             SetHandleSize(ServerList, ServerListSize+4);
  204.             *(unsigned long*)(*ServerList + ServerListSize) = addr;
  205.             ServerListSize += 4;
  206.         }
  207.         p += len+1;
  208.         if ((long)p & 1) p++;
  209.     }
  210.     HUnlock(h);
  211.     CloseResFile(rsrcRefNum);
  212.     if (!ServerListSize) return rslvNoServers;
  213.     
  214.     /* Initialize the cache. */
  215.     
  216.     Cache = NewHandle(0);
  217.     CacheSize = 0;
  218.     return noErr;
  219. }
  220.  
  221. #pragma segment rslv
  222.  
  223. /*_____________________________________________________________________
  224.  
  225.     CrackOctet - Crack an Octet from a String.
  226.     
  227.     Entry:    p = pointer into string.
  228.     
  229.     Exit:        function result = pointer to char following cracked octet,
  230.                     or nil if no octet found.
  231.                 *o = cracked octet.
  232. _____________________________________________________________________*/
  233.  
  234. static unsigned char *CrackOctet (unsigned char *p, short *o)
  235.  
  236. {
  237.     short            octet;            /* cracked octet */
  238.  
  239.     if (isdigit(*p) && (octet = atoi(p)) <= 255) {
  240.         p += strspn(p, "0123456789");
  241.         *o = octet;
  242.         return p;
  243.     }
  244.     return nil;
  245. }
  246.  
  247. /*_____________________________________________________________________
  248.  
  249.     SkipName - Skip Domain Name in Server Reply.
  250.     
  251.     Entry:    p = pointer into reply.
  252.                 pEnd = pointer to end of reply.
  253.     
  254.     Exit:        function result = updated pointer into reply, or 
  255.                     nil if error.
  256. _____________________________________________________________________*/
  257.  
  258. static unsigned char *SkipName (unsigned char *p, unsigned char *pEnd)
  259.  
  260. {
  261.     while (true) {
  262.         if (!*p) {
  263.             return p+1;
  264.         } else if (*p >= 0xc0) {
  265.             return p+2;
  266.         } else {
  267.             p += *p+1;
  268.             if (p > pEnd) return nil;
  269.         }
  270.     }
  271. }
  272.  
  273. /*_____________________________________________________________________
  274.  
  275.     ParseReply - Parse DNS Server Reply.
  276.     
  277.     Entry:    reply = pointer to reply.
  278.                 replyLen = length of reply.
  279.                 
  280.     Exit:        function result = error code:
  281.                 *addr = IP address from reply.
  282. _____________________________________________________________________*/
  283.  
  284. static OSErr ParseReply (DNSReply *reply, unsigned short replyLen, 
  285.     unsigned long *addr)
  286.     
  287. {
  288.     unsigned char    *replyEnd;            /* pointer to end of server reply */
  289.     short                servRCode;            /* server reply code */
  290.     unsigned char    *z;                    /* pointer into reply */
  291.     short                i;                        /* loop index */
  292.     short                rdLength;            /* reply rdata length */
  293.     unsigned long    typeClass;            /* type (A) and class (IN) codes */
  294.     
  295.     if (replyLen < 12) return rslvServError;
  296.     if (reply->id != QueryId) return rslvNotOurQuery;
  297.     if (!(reply->flags & 0x0080)) return rslvServError; /* recursion not available */
  298.     servRCode = reply->flags & 0xf;
  299.     if (servRCode == dnsNameError) {
  300.         return rslvNoSuchDomain;
  301.     } else if (servRCode) {
  302.         return rslvServError;
  303.     }
  304.     replyEnd = (unsigned char*)reply + replyLen;
  305.     z = &reply->sections;
  306.     for (i = 0; i < reply->qdCount; i++) {
  307.         if (!(z = SkipName(z, replyEnd))) return rslvServError;
  308.         z += 4;
  309.     }
  310.     for (i = 0; i < reply->anCount; i++) {
  311.         if (!(z = SkipName(z, replyEnd))) return rslvServError;
  312.         memcpy(&typeClass, z, 4);
  313.         z += 8;
  314.         memcpy(&rdLength, z, 2);
  315.         z += 2;
  316.         if (z + rdLength > replyEnd) return rslvServError;
  317.         if (typeClass == ourTypeClass && rdLength == 4) {
  318.             memcpy(addr, z, 4);
  319.             return noErr;
  320.         }
  321.         z += rdLength;
  322.     }
  323. }
  324.  
  325. /*_____________________________________________________________________
  326.  
  327.     ResolveOne - Resolve a Domain Name Via a Single DNS Server Query.
  328.     
  329.     Entry:    iopb = pointer to UDP io param block.
  330.                 serverAddress = IP address of DNS server.
  331.                 name = domain name (Pascal string).
  332.                 timeOut = timeout delay in seconds.
  333.                 checkCancel = pointer to check cancel function.
  334.     
  335.     Exit:        function result = error code.
  336.                 *addr = IP address.
  337. _____________________________________________________________________*/
  338.  
  339. static OSErr ResolveOne (UDPiopb *iopb, unsigned long serverAddress, 
  340.     Str255 name, short timeOut, 
  341.     rslv_CheckCancelPtr checkCancel, unsigned long *addr)
  342.  
  343. {
  344.     DNSQuery            query;                /* DNS query */
  345.     short                queryLen;            /* length of query */
  346.     short                len;                    /* length of string */
  347.     unsigned char    *p;                    /* pointer into domain name */
  348.     unsigned char    *q, *r;                /* pointers into query */
  349.     unsigned long    typeClass;            /* type (A) and class (IN) codes */
  350.     WDS                wds;                    /* write data structure */
  351.     OSErr                rCode;                /* result code */
  352.     DNSReply            *reply;                /* pointer to server reply */
  353.     unsigned short    replyLen;            /* length of server reply */
  354.     OSErr                bfrReturnCode;        /* result code from buffer return call */
  355.  
  356.     /* Format DNS query. */
  357.     
  358.     query.id = QueryId;
  359.     query.flags = 0x0100;    /* recursion desired */
  360.     query.qdCount = 1;
  361.     query.anCount = 0;
  362.     query.nsCount = 0;
  363.     query.arCount = 0;
  364.     len = *name;
  365.     p = name+1;
  366.     r = query.question;
  367.     q = query.question+1;
  368.     while(len--) {
  369.         if (*p == '.') {
  370.             *r = q-r-1;
  371.             if (!*r || *r > 0x3f) return rslvNameSyntaxErr;
  372.             r = q;
  373.             q++;
  374.         } else {
  375.             *q++ = *p;
  376.         }
  377.         p++;
  378.     }
  379.     *r = q-r-1;
  380.     if (!*r || *r > 0x3f) return rslvNameSyntaxErr;
  381.     *q++ = 0;
  382.     typeClass = ourTypeClass;
  383.     memcpy(q, &typeClass, 4);
  384.     queryLen = q + 4 - &query;
  385.     
  386.     /* Send query to server. */
  387.     
  388.     wds.length = queryLen;
  389.     wds.buffer = (Ptr)&query;
  390.     wds.zero = 0;
  391.     iopb->csCode = UDPWrite;
  392.     iopb->csParam.send.reserved = 0;
  393.     iopb->csParam.send.remoteHost = serverAddress;
  394.     iopb->csParam.send.remotePort = dnsPort;
  395.     iopb->csParam.send.wdsPtr = (Ptr)&wds;
  396.     iopb->csParam.send.checkSum = 0;
  397.     iopb->csParam.send.sendLength = 0;
  398.     if (rCode = PBControl((ParmBlkPtr)iopb, false)) return rCode;
  399.     
  400.     /* Wait for and process reply from server. */
  401.     
  402.     while (true) {
  403.         
  404.         /* Issue read and wait for reply */
  405.     
  406.         iopb->ioCompletion = nil;
  407.         iopb->csCode = UDPRead;
  408.         iopb->csParam.receive.timeOut = timeOut;
  409.         iopb->csParam.receive.secondTimeStamp = 0;
  410.         if (rCode = PBControl((ParmBlkPtr)iopb, true)) return rCode;
  411.         while (iopb->ioResult == 1) {
  412.             if (checkCancel && (*checkCancel)()) return rslvCancel;
  413.         }
  414.         if (rCode = iopb->ioResult) return rCode;
  415.         
  416.         /* Parse reply. */
  417.         
  418.         reply = (DNSReply*)iopb->csParam.receive.rcvBuff;
  419.         replyLen = iopb->csParam.receive.rcvBuffLen;
  420.         rCode = ParseReply(reply, replyLen, addr);
  421.         if (replyLen) {
  422.             iopb->csCode = UDPBfrReturn;
  423.             iopb->csParam.receive.rcvBuff = (Ptr)reply;
  424.             if (bfrReturnCode = PBControl((ParmBlkPtr)iopb, false)) return bfrReturnCode;
  425.         }
  426.         if (rCode != rslvNotOurQuery) return rCode;
  427.     }
  428. }
  429.  
  430. /*_____________________________________________________________________
  431.  
  432.     Resolve - Resolve a Domain Name Via DNS Server Queries.
  433.     
  434.     Entry:    name = domain name (Pascal string).
  435.                 checkCancel = pointer to check cancel function.
  436.     
  437.     Exit:        function result = error code.
  438.                 *addr = IP address.
  439. _____________________________________________________________________*/
  440.  
  441. static OSErr Resolve (Str255 name, 
  442.     rslv_CheckCancelPtr checkCancel, unsigned long *addr)
  443.     
  444. {
  445.     Ptr                rcvBuff;                /* pointer to receive buffer */
  446.     UDPiopb            iopb;                    /* UDP io param block */
  447.     UDPiopb            iopbRel;                /* UDP io param block for UDPRelease call */
  448.     short                timeOut;                /* timeout delay */
  449.     short                serverInx;            /* index in ServerList */
  450.     short                i;                        /* loop index */
  451.     unsigned long    serverAddress;        /* IP address of server */
  452.     OSErr                rCode;                /* result code */
  453.     OSErr                releaseCode;        /* UDPRelease call result code */
  454.  
  455.     /* Create UDP stream. */
  456.     
  457.     rcvBuff = NewPtr(rcvBuffSize);
  458.     iopb.ioCRefNum = RefNum;
  459.     iopb.csCode = UDPCreate;
  460.     iopb.csParam.create.rcvBuff = rcvBuff;
  461.     iopb.csParam.create.rcvBuffLen = rcvBuffSize;
  462.     iopb.csParam.create.notifyProc = nil;
  463.     iopb.csParam.create.localPort = 0;
  464.     if (rCode = PBControl((ParmBlkPtr)&iopb, false)) return rCode;
  465.     
  466.     /* Query the servers in round-robin order, doubling the timeout
  467.         delay each time (exponential backoff). */
  468.     
  469.     timeOut = initialTimeout;
  470.     serverInx = 0;
  471.     QueryId++;
  472.     for (i = 1; i <= maxQueries; i++) {
  473.         serverAddress = *(unsigned long*)(*ServerList + serverInx);
  474.         if (serverAddress) {
  475.             rCode = ResolveOne(&iopb, serverAddress, name, timeOut, checkCancel, addr);
  476.             if (rCode == rslvServError) {
  477.                 *(unsigned long*)(*ServerList + serverInx) = 0;
  478.             } else if (rCode != commandTimeout) {
  479.                 break;
  480.             } else if (i == maxQueries) {
  481.                 rCode = rslvNoResponse;
  482.                 break;
  483.             } else {
  484.                 timeOut <<= 1;
  485.             }
  486.         }
  487.         serverInx += 4;
  488.         if (serverInx >= ServerListSize) serverInx = 0;
  489.     }
  490.     
  491.     /* Release UDP stream. */
  492.     
  493.     iopbRel.ioCRefNum = RefNum;
  494.     iopbRel.udpStream = iopb.udpStream;
  495.     iopbRel.csCode = UDPRelease;
  496.     releaseCode = PBControl((ParmBlkPtr)&iopbRel, false);
  497.     DisposPtr(rcvBuff);
  498.     
  499.     return rCode ? rCode : releaseCode;
  500. }
  501.  
  502. /*_____________________________________________________________________
  503.  
  504.     rslv_Resolve - Resolve a Domain Name.
  505.     
  506.     Entry:    name = domain name (Pascal string).
  507.                 checkCancel = pointer to check cancel function.
  508.     
  509.     Exit:        function result = error code.
  510.                 *addr = IP address.
  511. _____________________________________________________________________*/
  512.  
  513. OSErr rslv_Resolve (Str255 name, rslv_CheckCancelPtr checkCancel,
  514.     unsigned long *addr)
  515.  
  516. {
  517.     Str255                nam;                /* copy of name */
  518.     unsigned char        *p;                /* pointer into domain name */
  519.     short                    o1,o2,o3,o4;    /* dotted decimal octets */
  520.     unsigned char        *c;                /* pointer into cache */
  521.     unsigned char        *cEnd;            /* pointer to end of cache */
  522.     short                    len;                /* length of string */
  523.     OSErr                    rCode;            /* error code */
  524.     
  525.     /* Check for dotted decimal notation. */
  526.     
  527.     utl_CopyPString(nam, name);
  528.     p2cstr(nam);
  529.     p = nam;
  530.     if ((p = CrackOctet(p, &o1)) && *p++ == '.' &&
  531.         (p = CrackOctet(p, &o2)) && *p++ == '.' &&
  532.         (p = CrackOctet(p, &o3)) && *p++ == '.' &&
  533.         (p = CrackOctet(p, &o4)) && !*p) {
  534.         *addr = (o1<<24) | (o2<<16) | (o3<<8) | o4;
  535.         return noErr;
  536.     }
  537.     
  538.     /* Append the default domain name extension if no dot in name. */
  539.     
  540.     p = nam;
  541.     if (!strchr(p, '.')) {
  542.         len = strlen(nam);
  543.         if (len + *Extension <= 255) {
  544.             memcpy(nam + len, Extension+1, *Extension);
  545.             *(nam + len + *Extension) = 0;
  546.         } else {
  547.             return rslvNameSyntaxErr;
  548.         }
  549.     }
  550.     c2pstr(nam);
  551.     
  552.     /* Check the cache. */
  553.     
  554.     c = *Cache;
  555.     cEnd = c + CacheSize;
  556.     while (c < cEnd) {
  557.         if (EqualString(c, nam, true, true)) {
  558.             c += *c + 1;
  559.             memcpy(addr, c, 4);
  560.             return noErr;
  561.         }
  562.         c += *c + 5;
  563.     }
  564.     
  565.     /* Resolve using the domain name servers. */
  566.     
  567.     if (rCode = Resolve(nam, checkCancel, addr)) return rCode;
  568.     
  569.     /* Add the new name/address pair to the cache. */
  570.     
  571.     len = *nam;
  572.     SetHandleSize(Cache, CacheSize + len + 5);
  573.     utl_CopyPString(*Cache + CacheSize, nam);
  574.     memcpy(*Cache + CacheSize + len + 1, addr, 4);
  575.     CacheSize += len + 5;
  576.     return noErr;
  577. }
  578.